<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Scripts\Tools;

use Srv\Apps\Proto\Enum\ErrCodeEnum;
use Srv\Libs\Boot;
use Srv\Libs\Common\CommFile;
use Srv\Libs\Common\CommJson;
use Srv\Libs\Common\CommString;
use Srv\Libs\Frame\Route;
use Srv\Libs\Tools\ReflectionClassData;
use Srv\Libs\Tools\ShellPrint;

require_once dirname(__DIR__, 2).'/Libs/Boot.php';
Boot::runConf('');

// 生成整体JSON
$allJsonData    = [
    'groupList'     => [],
    'enumList'      => [],
    'messageList'   => [],
    'errCodeList'   => [],
];

// 获取反射类MD内容
function getRefClassList(string $className, array &$classNameExtList):array
{
    $messageData            = ['comment' => '', 'fields' => []];
    $classNameExtList       = [];
    $refClassData           = ReflectionClassData::getRefDataByProto($className);
    if(!isset($refClassData['propertyList']) || count($refClassData['propertyList']) < 1) return $messageData;
    $messageData['comment'] = trim($refClassData['comment']);
    foreach($refClassData['propertyList'] as $propertyName => $propertySet){
        $isMust             = strpos(strtolower($propertySet['comment']), '[optional]') === false;
        $isArray            = strpos($propertySet['flag'], 'repeated') !== false;
        $type               = $propertySet['type'];
        if(strpos($type, '\\') !== false){
            $classNameExtList[] = $type;
            $type               = preg_replace("'\\\\'", '.', $type);
        }
        // 解码混合情况
        $messageList                = [];
        if(preg_match_all('/\[([A-Z][A-Za-z.]+(DTO|VO|BO))]/', $propertySet['comment'], $match)){
            $classNameNamespace     = CommString::getClassNameNamespace($className);
            foreach($match[1] as $commentClassName){
                if(strpos($commentClassName, '.') === false){
                    $subClassName   = $classNameNamespace.$commentClassName;
                }else{
                    $subClassName   = preg_replace('"."', '\\', 'Srv.Apps.Proto.'.$commentClassName);
                }
                $classNameExtList[] = $subClassName;
                $messageList[]      = str_replace('\\', '.', $subClassName);
            }
        }
        if(!(strtolower($type) === 'string' || substr($type, -4) === '.Any')) $messageList = [];
        $messageData['fields'][]    = [
            'name'          => $propertyName,
            'comment'       => trim($propertySet['comment']),
            'type'          => $type,
            'messageList'   => $messageList,
            'optional'      => $isMust ? 0 : 1,
            'repeated'      => $isArray ? 1 : 0,
        ];
    }
    return $messageData;
}

// 获取反射枚举类List
function getRefEnumList(string $className):array
{
    $enumData               = ['comment' => '', 'fields' => []];
    $refClassData           = ReflectionClassData::getRefDataByProto($className);
    if(!isset($refClassData['constantList']) || count($refClassData['constantList']) < 1) return $enumData;
    $enumData['comment']    = $refClassData['comment'];
    foreach($refClassData['constantList'] as $propertyName => $propertySet){
        if(substr($propertyName, 0, 1) === '_' || !isset($propertySet['value']) || !is_numeric($propertySet['value'])) continue;
        $enumData['fields'][] = [
            'name'          => $propertyName,
            'comment'       => trim($propertySet['comment']),
            'value'         => intval($propertySet['value']),
        ];
    }
    return $enumData;
}

// 组配置定义
$groupNameIgnoreList            = ['webs', 'socks', 'apiweb'];
$groupNameList                  = [
    'webs'      => ['platform' => ['web'], 'type' => 'http'],
    'apis'      => ['platform' => ['android', 'ios', 'web'], 'type' => 'http'],
    'socks'     => ['platform' => ['android', 'ios', 'web'], 'type' => 'websocket'],
    'apiweb'    => ['platform' => ['web'], 'type' => 'http'],
];
$routeList                      = Route::initData();
foreach ($routeList as $routeGroupList){
    foreach ($routeGroupList as $groupName => $pathList){
        ksort($pathList);
        foreach ($pathList as $pathUrl => $pathData){
            $path               = ltrim($pathUrl, '/');
//            $class              = $pathData[0];
//            $method             = $pathData[1];
            $auth               = $pathData[2];
            $code               = $pathData[3];
            $DTO                = $pathData[4];
            $VO                 = $pathData[5];
            $Comment            = $pathData[6];
            $ErrVOList          = $pathData[8];
            $codeStr            = $code;
            $logText            = str_pad($groupName.':'.$path, 39, ' ', STR_PAD_RIGHT);
            $logText            .= str_pad($auth, 5, ' ', STR_PAD_RIGHT);
            $logText            .= str_pad($codeStr, 13, ' ', STR_PAD_RIGHT);
            $logText            .= str_pad($DTO, 76, ' ', STR_PAD_LEFT);
            $logText            .= str_pad($VO, 60, ' ', STR_PAD_LEFT);
            $logText            .= ' '.str_pad($Comment, 42, ' ', STR_PAD_RIGHT);
            $logText            .= "\n";
            $codeList           = strlen($code) > 0 ? explode(',', $code) : [];
            $apiItem            = [
                'url'       => $path,
                'auth'      => $auth,
                'comment'   => $Comment,
                'inputs'    => strlen($DTO) < 1 ? [] : [preg_replace("'\\\\'", '.', $DTO)],
                'outputs'   => strlen($VO) < 1 ? [] : [preg_replace("'\\\\'", '.', $VO)],
                'codes'     => count($codeList) > 0 ? $codeList : null
            ];
            if(in_array($groupName, $groupNameIgnoreList, true)) $groupName = '_'.$groupName;
            if(!isset($allJsonData['groupList'][$groupName])){
                $allJsonData['groupList'][$groupName] = array_merge($groupNameList[$groupName]??[], ['apis' => []]);
            }
            if(count($ErrVOList) > 0) foreach ($ErrVOList as $ErrVO){
                if(strlen($ErrVO) < 1) continue;
                $ErrVO          = preg_replace("'\\\\'", '.', $ErrVO);
                $apiItem['outputs'][] = $ErrVO;
            }
            $allJsonData['groupList'][$groupName]['apis'][] = $apiItem;
            ShellPrint::printSuccessString($logText);
        }
    }
}

$allFileList            = [];
if(!CommFile::getAllFileList(Boot::getAppsPath().'Proto/', $allFileList) || count($allFileList) < 1){
    ShellPrint::printErrorString("Read Proto Path Fail\n");
}else{
    $rootPath           = Boot::getRootPath();
    $rootPathLength     = strlen($rootPath);
    $classNameList      = [];
    foreach ($allFileList as $filePath){
        if(substr($filePath, 0, $rootPathLength) === $rootPath && strpos($filePath, 'Metadata/') === false){
            $classNamePath      = substr($filePath, $rootPathLength);
            $className          = preg_replace("'/'", '\\', substr($classNamePath, 0, strrpos($classNamePath, '.')));
            $classNameList[]    = $className;
        }
    }
    if(count($classNameList) > 0){
        sort($classNameList);
        $classNameRefList           = [];
        foreach ($classNameList as $classNameItem){
            $classRefItemList       = [$classNameItem];
            while(count($classRefItemList) > 0){
                $className          = array_shift($classRefItemList);
                if(in_array($className, $classNameRefList, true) || strlen($className) < 1) continue;
                $classNameRefList[] = $className;
                $classNameExtList   = [];
                $classNameDot       = preg_replace("'\\\\'", '.', $className);
                if(substr($className, -4) === 'Enum'){
                    if($className !== ErrCodeEnum::class) $allJsonData['enumList'][$classNameDot] = getRefEnumList($className);
                }else{
                    $allJsonData['messageList'][$classNameDot]  = getRefClassList($className, $classNameExtList);
                }
                if(count($classNameExtList) > 0) array_unshift($classRefItemList, ...$classNameExtList);
            }
        }
    }
}

// 错误代码
$errCodeEnumList    = getRefEnumList(ErrCodeEnum::class);
foreach ($errCodeEnumList['fields']??[] as $errCodeEnumItem){
    $allJsonData['errCodeList'][] = [
        'code'      => $errCodeEnumItem['value'],
        'key'       => $errCodeEnumItem['name'],
        'comment'   => $errCodeEnumItem['comment'],
    ];
}

$filePath           = Boot::getTmpPath().'apis.json';
if(CommFile::writeFileString($filePath, CommJson::encodeArrayPretty($allJsonData), 'w')){
    ShellPrint::printSuccessString($filePath."\n");
}else{
    ShellPrint::printErrorString($filePath."\n");
}
echo "\n\n";
